package com.jiawa.train.business.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class RedisConfig {

    @Value("${spring.cache.redis.key-prefix}")
    private String keyPrefix;

    @Value("${spring.cache.redis.time-to-live}")
    private long time2Live;

    /**
     * 配置spring操作redis保存数据时的序列化方式, 默认是Java内置的序列化方式, 二进制形式;
     * 现在改用JSON序列化方式, 提高可读性.
     * @param redisConnectionFactory Redis连接工厂对象
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        //创建RedisTemplate对象
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        //设置Redis连接工厂对象
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 使用String序列化键
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        // 使用JSON序列化值
        redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());

        // 对于Hash结构，同样设置序列化器
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());

        redisTemplate.afterPropertiesSet();

        return redisTemplate;
    }

    /**
     * spring使用redis做缓存时的配置
     *   在这里配置 Key 前缀、缓存过期时间等
     * 注意:
     *   上面配置的JSON序列化方式, 对缓存是不生效的!
     *   需要配置CacheManager, 但是配置了CacheManager后, application.properties中的redis缓存配置就不生效了!
     *   因此需要在CacheManager中配置 Key 前缀、缓存过期时间等.
     * @param connectionFactory
     * @return
     */
    @Bean
    public CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        // 全局默认配置
        RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig()
                // 设置 Key 前缀（格式为 "cacheName:key"）
                .computePrefixWith(cacheName -> keyPrefix + ":" + cacheName + ":") // 新版本推荐使用 computePrefixWith
                //.prefixKeysWith("myCachePrefix::") // 旧版本设置固定前缀（所有缓存共享）
                // 缓存空值（默认不缓存）
                //.disableCachingNullValues() // 如果允许缓存 null，注释此行
                // 设置默认 TTL
                .entryTtl(Duration.ofHours(time2Live))
                // 序列化配置
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));

        // 针对不同缓存名称单独配置（例如 userCache 设置 1 小时 TTL）
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        cacheConfigurations.put("userCache", defaultConfig.entryTtl(Duration.ofHours(1)));
        cacheConfigurations.put("productCache", defaultConfig.entryTtl(Duration.ofHours(2)));

        return RedisCacheManager.builder(connectionFactory)
                .cacheDefaults(defaultConfig) // 默认配置
                .withInitialCacheConfigurations(cacheConfigurations) // 缓存级别配置
                .build();
    }
}